﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.iRods.Common;
using Renci.iRods.Packets;
using Renci.iRods.Exceptions;
using System.IO;
using System.Reflection;

namespace Renci.iRods.Data
{
    public class IRodsCollection : FileSystemDataItem
    {
        [iRodsColumn(DataObjectAttributes.COL_COLL_ID)]
        public long ID { get; internal set; }

        private string _name;
        /// <summary>
        /// Gets the name of the collection.
        /// </summary>
        public override string Name
        {
            get
            {
                if (this._name == null && this.FullName != null)
                {
                    this._name = System.IO.Path.GetFileName(this.FullName);
                }
                return this._name;
            }

            internal set
            {
                throw new NotSupportedException("Name property cannot be set.");
            }
        }

        /// <summary>
        /// Gets the full name of the collection.
        /// </summary>
        [iRodsColumn(DataObjectAttributes.COL_COLL_NAME)]
        public override string FullName { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_PARENT_NAME)]
        public string ParentName { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_OWNER_NAME)]
        public string OwnerName { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_OWNER_ZONE)]
        public string OwnerZone { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_MAP_ID)]
        public long MapId { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_INHERITANCE)]
        public string Inheritance { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_COMMENTS)]
        public string Comments { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_CREATE_TIME)]
        public override DateTime CreationTime { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_MODIFY_TIME)]
        public override DateTime LastWriteTime { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_TYPE)]
        public string TypeName { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_INFO1)]
        public string Info1 { get; internal set; }

        [iRodsColumn(DataObjectAttributes.COL_COLL_INFO2)]
        public string Info2 { get; internal set; }

        protected override string MetadataFullName
        {
            get { return this.FullName; }
        }

        protected override IRodsQuery MetadataQuery
        {
            get
            {
                var query = new IRodsQuery();
                query.AddOutputColumn(DataObjectAttributes.COL_META_COLL_ATTR_ID);
                query.AddOutputColumn(DataObjectAttributes.COL_META_NAMESPACE_COLL);
                query.AddOutputColumn(DataObjectAttributes.COL_META_COLL_ATTR_NAME);
                query.AddOutputColumn(DataObjectAttributes.COL_META_COLL_ATTR_VALUE);
                query.AddOutputColumn(DataObjectAttributes.COL_META_COLL_ATTR_UNITS);
                query.AddCondition(DataObjectAttributes.COL_COLL_NAME, string.Format("='{0}'", this.FullName));

                return query;
            }
        }

        protected override Func<IRodsDataReader, MetaData> MetadataProjector
        {
            get
            {
                return (r) =>
                {
                    return new MetaData(
                        r.GetInt32(DataObjectAttributes.COL_META_COLL_ATTR_ID),
                        r.GetString(DataObjectAttributes.COL_META_NAMESPACE_COLL),
                        r.GetString(DataObjectAttributes.COL_META_COLL_ATTR_NAME),
                        r.GetString(DataObjectAttributes.COL_META_COLL_ATTR_VALUE),
                        r.GetString(DataObjectAttributes.COL_META_COLL_ATTR_UNITS));
                };
            }
        }

        internal IRodsCollection(IRodsConnection connection)
            : this(connection, string.Empty)
        {

        }

        public IRodsCollection(IRodsConnection connection, string fullName)
            : base(connection, fullName, "-C")
        {
        }

        /// <summary>
        /// Creates a collection.
        /// </summary>
        public void Create()
        {
            var request = new CollectionInputNewPacket(this.FullName);

            this.Connection.SendRequest(RequestCodes.COLL_CREATE_AN, request);

            var response = this.Connection.ReceiveMessage();

            if (response.Exception != null)
            {
                throw response.Exception;
            }
        }

        /// <summary>
        /// Creates a sub-collection or sub-collections on the specified path. The specified path can be relative to this instance of the <see cref="IRodsCollection"/> class.
        /// </summary>
        /// <param name="path">The specified path.</param>
        /// <returns>The last collection specified in path.</returns>
        public IRodsCollection CreateSubdirectory(string path)
        {
            var request = new CollectionInputNewPacket(this.Connection.ParsePath(path, this.FullName));

            request.AddKey("recursiveOpr");

            this.Connection.SendRequest(RequestCodes.COLL_CREATE_AN, request);

            var response = this.Connection.ReceiveMessage();

            if (response.Exception != null)
            {
                throw response.Exception;
            }

            return new IRodsCollection(this.Connection, request.CollectionName);
        }

        public override void Delete()
        {
            this.Delete(false);
        }

        /// <summary>
        /// Deletes this instance of a <see cref="IRodsCollection"/>, specifying whether to delete subdirectories and files.
        /// </summary>
        /// <param name="recursive">if set to <c>true</c> [recursive].</param>
        public void Delete(bool recursive)
        {
            if (!this.Exists)
                throw new FileNotFoundException(string.Format("Collection '{0}' not found."));

            var request = new CollectionInputNewPacket(this.FullName);

            if (recursive)
            {
                request.AddKey("recursiveOpr");
            }

            this.Connection.SendRequest(RequestCodes.RM_COLL_AN, request);

            var response = this.Connection.ReceiveMessage<CollectionOperationStatusPacket>();

            if (response.Exception != null)
            {
                throw response.Exception;
            }

            if (!(response.Body.Path == this.FullName && response.Body.TotalFileCounter == 1 && response.Body.FilesCounter == 1))
                throw new IRodsException("Delete operation was executed but has invalid state.");
        }

        public override void Refresh()
        {
            this.Exists = this.Load((q) => { q.AddCondition(DataObjectAttributes.COL_COLL_NAME, string.Format("='{0}'", this.FullName)); });
        }
    }
}
